﻿using System;
using System.Threading;
using System.Threading.Tasks;
using NUnit.Framework;

namespace WSU.ChekaParallel.TestThread.Sync
{
    sealed class TestLock
    {
        // --------------------------------------------- //
        #region [ member variables ]

        private static Random m_rand = new Random();

        private int m_sum;

        private readonly int m_numWorker;
        private readonly int m_addPerWorker;

        private readonly object m_lock;

        #endregion

        // --------------------------------------------- //
        public TestLock(int numWorker, int addPerWorker)
        {
            m_numWorker = numWorker;
            m_addPerWorker = addPerWorker;

            m_sum = 0;
            m_lock = new object();
        }

        public int Expected { get { return m_numWorker * m_addPerWorker; } }
        public int ActualSum { get { return m_sum; } }

        // --------------------------------------------- //
        #region [ entry ]

        public int Run()
        {
            int localsum = 0;

            Task[] tasks = new Task[m_numWorker];

            for (int index = 0; index < m_numWorker; ++index)
            {
                tasks[index] = Task.Factory.StartNew(() =>
                {
                    // because this closure has used member field, so the closure
                    // will encapsulate "this" as its member field
                    // so all threads share the same "this", then same "m_sum" and same "m_lock"
                    for (int counter = 0; counter < m_addPerWorker; ++counter)
                    {
                        lock (m_lock)
                        {
                            Thread.Sleep(m_rand.Next(10, 30));
                            ++m_sum;
                        }
                        ++localsum;
                    }
                });
            }

            Task.WaitAll(tasks);

            return localsum;
        }

        #endregion
    }

    [TestFixture]
    sealed class LockUnitTest
    {
        // --------------------------------------------- //
        #region [ test ]

        [Test]
        public void TestSyncByLocking()
        {
            TestLock locktester = new TestLock(5, 10);
            int localsum = locktester.Run();
            Assert.AreEqual(locktester.Expected, locktester.ActualSum);
            Console.WriteLine("expected = {0},localsum = {1}", locktester.Expected.ToString(), localsum.ToString());
        }

        #endregion
    }
}